// Copyright Epic Games, Inc. All Rights Reserved.

#include "SHitMarkerConfirmationWidget.h"

#include "Weapons/LyraWeaponStateComponent.h"

class FPaintArgs;

/**
 * 构造函数
 */
SHitMarkerConfirmationWidget::SHitMarkerConfirmationWidget()
{
}

/**
 * 构建控件
 * @param InArgs Slate参数
 * @param InContext 本地玩家上下文
 * @param ZoneOverrideImages 区域覆盖图像
 */
void SHitMarkerConfirmationWidget::Construct(const FArguments& InArgs, const FLocalPlayerContext& InContext, const TMap<FGameplayTag, FSlateBrush>& ZoneOverrideImages)
{
	PerHitMarkerImage = InArgs._PerHitMarkerImage; //设置每个命中标记图像
	PerHitMarkerZoneOverrideImages = ZoneOverrideImages; //设置每个命中标记区域覆盖图像
	AnyHitsMarkerImage = InArgs._AnyHitsMarkerImage; //设置任意命中标记图像
	bColorAndOpacitySet = InArgs._ColorAndOpacity.IsSet(); //设置颜色和不透明度是否设置
	ColorAndOpacity = InArgs._ColorAndOpacity; //设置颜色和不透明度

	MyContext = InContext; //设置我的上下文
}

/**
 * 绘制控件
 * @param Args 绘制参数
 * @param AllottedGeometry 分配的几何信息
 * @param MyCullingRect 剔除矩形
 * @param OutDrawElements 绘制元素列表
 * @param LayerId 图层ID
 * @param InWidgetStyle 控件样式
 * @param bParentEnabled 父控件是否启用
 * @return 图层ID
 */
int32 SHitMarkerConfirmationWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
{
	const bool bIsEnabled = ShouldBeEnabled(bParentEnabled); //是否启用
	const ESlateDrawEffect DrawEffects = bIsEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect; //绘制效果
	const FVector2D LocalCenter = AllottedGeometry.GetLocalPositionAtCoordinates(FVector2D(0.5f, 0.5f)); //本地中心

	const bool bDrawMarkers = (HitNotifyOpacity > KINDA_SMALL_NUMBER); //是否绘制标记

	if (bDrawMarkers) //如果需要绘制标记
	{
		// 检查是否应使用屏幕空间伤害位置命中提示
		TArray<FLyraScreenSpaceHitLocation> LastWeaponDamageScreenLocations; //最后武器伤害屏幕位置
		if (APlayerController* PC = MyContext.IsInitialized() ? MyContext.GetPlayerController() : nullptr) //如果玩家控制器有效
		{
			if (ULyraWeaponStateComponent* WeaponStateComponent = PC->FindComponentByClass<ULyraWeaponStateComponent>()) //如果找到武器状态组件
			{
				WeaponStateComponent->GetLastWeaponDamageScreenLocations(/*out*/ LastWeaponDamageScreenLocations); //获取最后武器伤害屏幕位置
			}
		}

		if ((LastWeaponDamageScreenLocations.Num() > 0) && (PerHitMarkerImage != nullptr)) //如果有伤害位置且每个命中标记图像有效
		{
			const FVector2D HalfAbsoluteSize = AllottedGeometry.GetAbsoluteSize() * 0.5f; //一半绝对大小

			for (const FLyraScreenSpaceHitLocation& Hit : LastWeaponDamageScreenLocations) //遍历伤害位置
			{
				const FSlateBrush* LocationMarkerImage = PerHitMarkerZoneOverrideImages.Find(Hit.HitZone); //查找位置标记图像
				if (LocationMarkerImage == nullptr) //如果未找到
				{
					LocationMarkerImage = PerHitMarkerImage; //使用默认每个命中标记图像
				}

				FLinearColor MarkerColor = bColorAndOpacitySet ?
					ColorAndOpacity.Get().GetColor(InWidgetStyle) : //获取设置的颜色
					(InWidgetStyle.GetColorAndOpacityTint() * LocationMarkerImage->GetTint(InWidgetStyle)); //使用画刷色调
				MarkerColor.A *= HitNotifyOpacity; //乘以不透明度

				const FVector2D WindowSSLocation = Hit.Location + MyCullingRect.GetTopLeft(); // 不在全屏模式下时考虑窗口装饰
				const FSlateRenderTransform DrawPos(AllottedGeometry.AbsoluteToLocal(WindowSSLocation)); //绘制位置

				const FPaintGeometry Geometry(AllottedGeometry.ToPaintGeometry(LocationMarkerImage->ImageSize, FSlateLayoutTransform(-(LocationMarkerImage->ImageSize * 0.5f)), DrawPos)); //绘制几何
				FSlateDrawElement::MakeBox(OutDrawElements, LayerId, Geometry, LocationMarkerImage, DrawEffects, MarkerColor); //绘制盒子
			}
		}
		
		if (AnyHitsMarkerImage != nullptr) //如果任意命中标记图像有效
		{
			FLinearColor MarkerColor = bColorAndOpacitySet ?
				ColorAndOpacity.Get().GetColor(InWidgetStyle) : //获取设置的颜色
				(InWidgetStyle.GetColorAndOpacityTint() * AnyHitsMarkerImage->GetTint(InWidgetStyle)); //使用画刷色调
			MarkerColor.A *= HitNotifyOpacity; //乘以不透明度

			// 否则在十字准星中心显示命中提示
			const FPaintGeometry Geometry(AllottedGeometry.ToPaintGeometry(AnyHitsMarkerImage->ImageSize, FSlateLayoutTransform(LocalCenter - (AnyHitsMarkerImage->ImageSize * 0.5f)))); //绘制几何
			FSlateDrawElement::MakeBox(OutDrawElements, LayerId, Geometry, AnyHitsMarkerImage, DrawEffects, MarkerColor); //绘制盒子
		}
	}

	return LayerId; //返回图层ID
}

/**
 * 计算期望大小
 * @param LayoutScaleMultiplier 布局缩放乘数
 * @return 期望大小
 */
FVector2D SHitMarkerConfirmationWidget::ComputeDesiredSize(float) const
{
	return FVector2D(100.0f, 100.0f); //返回固定大小
}

/**
 * Tick函数
 * @param AllottedGeometry 分配的几何信息
 * @param InCurrentTime 当前时间
 * @param InDeltaTime 增量时间
 */
void SHitMarkerConfirmationWidget::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
{
	HitNotifyOpacity = 0.0f; //重置命中提示不透明度

	if (APlayerController* PC = MyContext.IsInitialized() ? MyContext.GetPlayerController() : nullptr) //如果玩家控制器有效
	{
		if (ULyraWeaponStateComponent* DamageMarkerComponent = PC->FindComponentByClass<ULyraWeaponStateComponent>()) //如果找到伤害标记组件
		{
			const double TimeSinceLastHitNotification = DamageMarkerComponent->GetTimeSinceLastHitNotification(); //获取自上次命中通知以来的时间
			if (TimeSinceLastHitNotification < HitNotifyDuration) //如果在持续时间内
			{
				HitNotifyOpacity = FMath::Clamp(1.0f - (float)(TimeSinceLastHitNotification / HitNotifyDuration), 0.0f, 1.0f); //计算不透明度
			}
		}
	}
}